﻿//******************************************************************************************************
//  Program.cs - Gbtc
//
//  Copyright © 2018, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
//  file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://opensource.org/licenses/MIT
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  05/22/2018 - J. Ritchie Carroll
//       Generated original version of source code.
//
//******************************************************************************************************

using System.IO;
using System.Text;

namespace GenGoFuncRefInstances
{
    class Program
    {
        static void Main(string[] args)
        {
            // More than 16 reference parameters (not counting any normal parameters) and I begin
            // to question if code was generated by a human being
            const int MaximumParameters = 16;
            const string TypeDefinitions = ", TRef{0}";
            const string ParameterDefinitions = ", ref TRef{0} ref{0}";
            const string FunctionParameters = ", ref ref{0}";
            const string ParameterDocs = "/// <param name=\"ref{0}\">Reference parameter {0}.</param>";

            // {0} = Current Length (> 1)
            // {1} = Type Definitions, e.g., " TRef2,"
            // {2} = Parameter Definitions, e.g., " ref TRef2 ref2,"
            // {3} = Function Parameters, e.g., " ref ref2,"
            const string GoFuncSource =
 @"
    /// <summary>
    /// Represents a Go function execution context with {0} reference parameters for handling ""defer"", ""panic"", and ""recover"" keywords.
    /// </summary>
    public sealed class GoFunc<TRef1{1}, T> : GoFunc<T>
    {{
        public delegate void GoRefAction(ref TRef1 ref1{2}, Defer defer, Panic panic, Recover recover);
        public delegate T GoRefFunction(ref TRef1 ref1{2}, Defer defer, Panic panic, Recover recover);

        private readonly GoRefFunction m_function;

        [MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerNonUserCode]
        public GoFunc(GoRefAction action) => m_function = (ref TRef1 ref1{2}, Defer defer, Panic panic, Recover recover) =>
        {{
            action(ref ref1{3}, defer, panic, recover);
            return default;
        }};

        [MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerNonUserCode]
        public GoFunc(GoRefFunction function) => m_function = function;

        [MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerStepperBoundary]
        public T Execute(ref TRef1 ref1{2})
        {{
            T result = default;

            try
            {{
                result = m_function(ref ref1{3}, HandleDefer, HandlePanic, HandleRecover);
            }}
            catch (PanicException ex)
            {{
                CapturedPanic.Value = ex;
            }}
            finally
            {{
                HandleFinally();
            }}

            return result;
        }}
    }}
";

            // {0} = Current Length (> 1)
            // {1} = Type Definitions, e.g., " TRef2,"
            // {2} = Parameter Definitions, e.g., " ref TRef2 ref2,"
            // {3} = Function Parameters, e.g., " ref ref2,"
            // {4} = Parameter Docs, e.g., "\r\n/// <param name=""ref2"">Reference parameter 2.</param>"
            const string GoActionHelper =
@"
        /// <summary>
        /// Executes a Go function with {0} reference parameters and no return value.
        /// </summary>
        /// <param name=""ref1"">Reference parameter 1.</param>{4}
        /// <param name=""action"">Go function to execute called with defer, panic and recover function references.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerNonUserCode]
        public static void func<TRef1{1}>(ref TRef1 ref1{2}, GoFunc<TRef1{1}, object>.GoRefAction action) => new GoFunc<TRef1{1}, object>(action).Execute(ref ref1{3});
";

            // {0} = Current Length (> 1)
            // {1} = Type Definitions, e.g., " TRef2,"
            // {2} = Parameter Definitions, e.g., " ref TRef2 ref2,"
            // {3} = Function Parameters, e.g., " ref ref2,"
            // {4} = Parameter Docs, e.g., "\r\n/// <param name=""ref2"">Reference parameter 2.</param>"
            const string GoFuncHelper =
@"
        /// <summary>
        /// Executes a Go function with {0} reference parameters and a return value.
        /// </summary>
        /// <param name=""ref1"">Reference parameter 1.</param>{4}
        /// <param name=""function"">Go function to execute called with defer, panic and recover function references.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerNonUserCode]
        public static T func<TRef1{1}, T>(ref TRef1 ref1{2}, GoFunc<TRef1{1}, T>.GoRefFunction function) => new GoFunc<TRef1{1}, T>(function).Execute(ref ref1{3});
";

            StringBuilder goFuncRefSource = new StringBuilder();
            string typeDefinitions = "";
            string parameterDefinitions = "";
            string functionParameters = "";
            string parameterDocs;

            for (int i = 2; i <= MaximumParameters; i++)
            {
                typeDefinitions += string.Format(TypeDefinitions, i);
                parameterDefinitions += string.Format(ParameterDefinitions, i);
                functionParameters += string.Format(FunctionParameters, i);

                goFuncRefSource.AppendFormat(GoFuncSource, i, typeDefinitions, parameterDefinitions, functionParameters);
            }

            using (StreamWriter writer = File.CreateText("GoFuncRef.cs"))
                writer.Write(goFuncRefSource.ToString());

            StringBuilder helperSource = new StringBuilder();
            typeDefinitions = "";
            parameterDefinitions = "";
            functionParameters = "";
            parameterDocs = "";

            for (int i = 2; i <= MaximumParameters; i++)
            {
                typeDefinitions += string.Format(TypeDefinitions, i);
                parameterDefinitions += string.Format(ParameterDefinitions, i);
                functionParameters += string.Format(FunctionParameters, i);
                parameterDocs += $"\r\n        {string.Format(ParameterDocs, i)}";

                helperSource.AppendFormat(GoActionHelper, i, typeDefinitions, parameterDefinitions, functionParameters, parameterDocs);
                helperSource.AppendFormat(GoFuncHelper, i, typeDefinitions, parameterDefinitions, functionParameters, parameterDocs);
            }

            using (StreamWriter writer = File.CreateText("BuiltInGoFuncRef.cs"))
                writer.Write(helperSource.ToString());
        }
    }
}
